//
//  AudioCaptureSessionCenter.swift
//  AgoraPremium
//
//  Created by GongYuhua on 2017/10/25.
//  Copyright © 2017年 Agora. All rights reserved.
//

import Foundation
import AVFoundation

protocol AudioCaptureSessionCenterDelegate: NSObjectProtocol {
    func audioCaptureSessionCenter(center: AudioCaptureSessionCenter, didCaptureBuffer buffer: CMSampleBuffer)
}

class AudioCaptureSessionCenter: NSObject {
    weak var delegate: AudioCaptureSessionCenterDelegate?
    
    private var captureSession: AVCaptureSession?
    private var captureAudioDeviceInput: AVCaptureDeviceInput?
    private var captureAudioDataOutput: AVCaptureAudioDataOutput?
    
    init(delegate: AudioCaptureSessionCenterDelegate?) {
        super.init()
        self.delegate = delegate
    }
    
    func setupCapture(sampleRate:UInt, numberOfChannels:UInt) -> Bool {
        guard let audioDevice = AVCaptureDevice.default(for: .audio), audioDevice.isConnected,
            let captureAudioDeviceInput = try? AVCaptureDeviceInput(device: audioDevice) else {
            return false
        }
        
        let captureSession = AVCaptureSession()
        self.captureSession = captureSession
        
        if captureSession.canAddInput(captureAudioDeviceInput) {
            captureSession.addInput(captureAudioDeviceInput)
            self.captureAudioDeviceInput = captureAudioDeviceInput
        } else {
            return false
        }
        
        let captureAudioDataOutput = AVCaptureAudioDataOutput()
        
        if captureSession.canAddOutput(captureAudioDataOutput) {
            captureSession.addOutput(captureAudioDataOutput)
            self.captureAudioDataOutput = captureAudioDataOutput
            let settings = [
                        AVFormatIDKey: kAudioFormatLinearPCM,
                        AVNumberOfChannelsKey : numberOfChannels,
                AVSampleRateKey : sampleRate] as [String : Any]
            self.captureAudioDataOutput?.audioSettings = settings
        } else {
            return false
        }
        
        
        let audioDataOutputQueue = DispatchQueue(label: "AudioDataOutputQueue")
        captureAudioDataOutput.setSampleBufferDelegate(self, queue: audioDataOutputQueue)
        
        return true
    }
    
    private var retryCount = 0
    
    func startCapture() {
        guard let captureSession = captureSession else {
            return
        }
        
    #if os(iOS)
        if captureSession.isInterrupted {
            if retryCount < 3 {
                retryCount += 1
                DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { [weak self] in
                    self?.startCapture()
                })
                return
            } else {
                let reseted = resetCapture()
                if !reseted {
                    return
                }
            }
        }
    #endif
        
        if !captureSession.isRunning {
            captureSession.startRunning()
            retryCount = 0
        }
    }
    
    func stopCapture() {
        guard let captureSession = captureSession else {
            return
        }
        
        if captureSession.isRunning {
            captureSession.stopRunning()
        }
    }
    
    func deallocCapture() {
        captureAudioDataOutput?.setSampleBufferDelegate(nil, queue: nil)
    }
    
    deinit {
        stopCapture()
        deallocCapture()
    }
}

private extension AudioCaptureSessionCenter {
    func resetCapture() -> Bool {
        guard let captureAudioDeviceInput = captureAudioDeviceInput, let captureAudioDataOutput = captureAudioDataOutput else {
            return false
        }
        
        let captureSession = AVCaptureSession()
        self.captureSession = captureSession

        if captureSession.canAddInput(captureAudioDeviceInput) {
            captureSession.addInput(captureAudioDeviceInput)
        } else {
            return false
        }

        if captureSession.canAddOutput(captureAudioDataOutput) {
            captureSession.addOutput(captureAudioDataOutput)
        } else {
            return false
        }
        
        return true
    }
}

extension AudioCaptureSessionCenter: AVCaptureAudioDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let _ = output as? AVCaptureAudioDataOutput else {
            return
        }
        
        delegate?.audioCaptureSessionCenter(center: self, didCaptureBuffer: sampleBuffer)
    }
}
